#include "ffmpegPlayer.h"

static enum AVPixelFormat s_stHWPixFmt;
enum AVPixelFormat GetHWFormat(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts)
{
	const enum AVPixelFormat *p;

	for (p = pix_fmts; *p != -1; p++) {
		if (*p == s_stHWPixFmt)
			return *p;
	}

	fprintf(stderr, "Failed to get HW surface format.\n");
	return AV_PIX_FMT_NONE;
}

C_FfmpegPlayer::C_FfmpegPlayer()
{
	pFmtCtx = NULL;
	pCodecCtx = NULL;
	pStream = NULL;
	pCodec = NULL;
	pHWDeviceCtx = NULL;
}

C_FfmpegPlayer::~C_FfmpegPlayer()
{
	av_packet_free(&pPacket);
	avcodec_free_context(&pCodecCtx);
	avformat_close_input(&pFmtCtx);
	av_buffer_unref(&pHWDeviceCtx);
}

void C_FfmpegPlayer::SetFilePath(const char *filePath)
{
	pMediaPath = filePath;
}

void C_FfmpegPlayer::SetHwdevice(const char *hwdeviceName)
{
	pHWDevice = hwdeviceName;
}

int C_FfmpegPlayer ::InitHWDecoder(AVCodecContext *ctx, const enum AVHWDeviceType type)
{
	int err = 0;

	if ((err = av_hwdevice_ctx_create(&pHWDeviceCtx, type,
									  NULL, NULL, 0)) < 0) {
		fprintf(stderr, "Failed to create specified HW device.\n");
		return err;
	}
	ctx->hw_device_ctx = av_buffer_ref(pHWDeviceCtx);

	return err;
}

int C_FfmpegPlayer::InitFfmpeg()
{
	int ret;
	enum AVHWDeviceType type;
	int i;

	type = av_hwdevice_find_type_by_name(pHWDevice);
	if (type == AV_HWDEVICE_TYPE_NONE) {
		fprintf(stderr, "Device type %s is not supported.\n", pHWDevice);
		fprintf(stderr, "Available device types:");
		while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
			fprintf(stderr, " %s", av_hwdevice_get_type_name(type));
		fprintf(stderr, "\n");
		return -1;
	}

	pPacket = av_packet_alloc();
	if (!pPacket) {
		fprintf(stderr, "Failed to allocate AVPacket\n");
		return -1;
	}

	/* open the input file */
	if (avformat_open_input(&pFmtCtx, pMediaPath, NULL, NULL) != 0) {
		fprintf(stderr, "Cannot open input file '%s'\n", pMediaPath);
		return -1;
	}

	if (avformat_find_stream_info(pFmtCtx, NULL) < 0) {
		fprintf(stderr, "Cannot find input stream information.\n");
		return -1;
	}

	av_dump_format(pFmtCtx, 0, pMediaPath, 0);

	/* find the video stream information */
	ret = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	if (ret < 0) {
		fprintf(stderr, "Cannot find a video stream in the input file\n");
		return -1;
	}
	iVideoStream = ret;

	pCodec = avcodec_find_decoder_by_name("h264_rkmpp");
	if (pCodec == NULL) {
		std::cout << "Cann't find codec!" << std::endl;
		return -1;
	}

	stHWPixFmt = AV_PIX_FMT_DRM_PRIME;

	std::cout << "hw_pix_fmt: " << stHWPixFmt << std::endl;

	if (!(pCodecCtx = avcodec_alloc_context3(pCodec))) {
		printf("avcodec_alloc_context3() failed %d\n", AVERROR(ENOMEM));
		return AVERROR(ENOMEM);
	}

	pStream = pFmtCtx->streams[iVideoStream];
	iFrameRate = pStream->avg_frame_rate.num / pStream->avg_frame_rate.den;
	if (ret = avcodec_parameters_to_context(pCodecCtx, pStream->codecpar) < 0) {
		printf("avcodec_parameters_to_context() failed %d\n", ret);
		return -1;
	}

	s_stHWPixFmt = stHWPixFmt;
	pCodecCtx->get_format = GetHWFormat;

	if (InitHWDecoder(pCodecCtx, type) < 0) {
		printf("InitHWDecoder failed \n");
		return -1;
	}

	if (ret = avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
		fprintf(stderr, "Failed to open codec for stream #%u\n", iVideoStream);
		return -1;
	}

	return 0;
}

// 提取 dma-buf fd
static int GetDmaBufFd(AVFrame *frame)
{
	AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)frame->data[0];
	if (!desc || desc->nb_objects < 1) {
		fprintf(stderr, "Invalid DRM PRIME frame descriptor\n");
		return -1;
	}
	// printf("desc->objects[0].fd = %d\r\n", desc->objects[0].fd);

	// 返回第一个对象的 fd
	return desc->objects[0].fd;
}

// 获取drm帧的格式信息 
static int32_t GetDRMFrameFormat(AVFrame *frame)
{
	AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)frame->data[0];
	if (!desc || desc->nb_objects < 1) {
		fprintf(stderr, "Invalid DRM PRIME frame descriptor\n");
		return -1;
	}

	return desc->layers[0].format;
}

// 解码+渲染
static int DecodeWrite(AVCodecContext *Avctx, AVPacket *Packet, enum AVPixelFormat HwPixFmt, C_DRMDisplay &cDRMDisplay, int frequency)
{
	AVFrame *pFrame = NULL;
	int size;
	int ret = 0;

	ret = avcodec_send_packet(Avctx, Packet);
	if (ret < 0) {
		fprintf(stderr, "Error during decoding\n");
		return ret;
	}

	while (1) {
		if (!(pFrame = av_frame_alloc())) {
			fprintf(stderr, "Can not alloc frame\n");
			ret = AVERROR(ENOMEM);
			goto fail;
		}

		ret = avcodec_receive_frame(Avctx, pFrame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
			goto fail;
		} else if (ret < 0) {
			fprintf(stderr, "Error while decoding\n");
			goto fail;
		}

		// 渲染drm的frame到drm的framebuff中
		if (pFrame->format == HwPixFmt) {
			int DmaBufFd = GetDmaBufFd(pFrame);
			uint32_t Format = GetDRMFrameFormat(pFrame);
			// 初始化 DRM fb
			if (!cDRMDisplay.CheckFramebuffExist(DmaBufFd)) {
				if (cDRMDisplay.InitDRMFb(pFrame, Format))
					goto fail;
			}

			if (DmaBufFd < 0) {
				fprintf(stderr, "Failed to get dma-buf fd\n");
				ret = -1;
				goto fail;
			}
			usleep(frequency * 1000);
			cDRMDisplay.RenderFrameWithDRM(DmaBufFd, pFrame->width, pFrame->height);
		}

	fail:
		av_frame_free(&pFrame);
		if (ret < 0)
			return ret;
	}
}

int C_FfmpegPlayer::Play(C_DRMDisplay cDRMDisplay)
{
	int frequency = 1000 / iFrameRate;
	std::cout << "frequency: " << frequency << std::endl;
	/* actual decoding and dump the raw data */
	while (1) {
		if ((av_read_frame(pFmtCtx, pPacket) < 0))
			break;

		if (iVideoStream == pPacket->stream_index)
			DecodeWrite(pCodecCtx, pPacket, stHWPixFmt, cDRMDisplay, frequency);

		av_packet_unref(pPacket);
	}
	return 0;
}